package com.fangyi.yunmusic.service.impl;

import com.fangyi.yunmusic.config.ResourceConfig;
import com.fangyi.yunmusic.constant.CollectionTypeConstant;
import com.fangyi.yunmusic.constant.OrderConstant;
import com.fangyi.yunmusic.exception.YunMusicException;
import com.fangyi.yunmusic.mapper.*;
import com.fangyi.yunmusic.pojo.*;
import com.fangyi.yunmusic.pojo.vo.PlaylistDetailVO;
import com.fangyi.yunmusic.pojo.vo.PlaylistVO;
import com.fangyi.yunmusic.pojo.vo.SongVO;
import com.fangyi.yunmusic.pojo.vo.UserVO;
import com.fangyi.yunmusic.service.PlaylistService;
import com.fangyi.yunmusic.service.SongService;
import com.fangyi.yunmusic.utils.FileServerUtils;
import com.fangyi.yunmusic.utils.PagedResult;
import com.fangyi.yunmusic.utils.PojoUtils;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.cache.LoadingCache;
import org.apache.commons.lang3.StringUtils;
import org.n3r.idworker.Id;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collector;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;

@Service
public class PlaylistServiceImpl implements PlaylistService {

    @Autowired
    private PlaylistMapper playlistMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private SongMapper songMapper;

    @Autowired
    private PlaylistHasSongMapper playlistHasSongMapper;

    @Autowired
    private SubscribeMapper subscribeMapper;

    @Autowired
    private SongService songService;

    @Autowired
    private ResourceConfig resourceConfig;

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public Integer addPlayCount(String playlistId) {
        Playlist playlist = playlistMapper.selectByPrimaryKey(playlistId);
        playlist.setPlayCount(playlist.getPlayCount() + 1);
        playlistMapper.updateByPrimaryKey(playlist);
        return playlist.getPlayCount();
    }

    @Override
    public String insertPlaylist(Playlist playlist) {
        if (StringUtils.isBlank(playlist.getName())) {
            throw new YunMusicException("歌单必须有名字");
        }
        if (StringUtils.isBlank(playlist.getUserId())) {
            throw new YunMusicException("用户Id为空");
        }
        playlist.setId(Id.nextString());
        Date date = new Date();
        playlist.setCreateTime(date);
        playlist.setUpdateTime(date);
        playlistMapper.insertSelective(playlist);
        return playlist.getId();
    }

    @Override
    public void deletePlaylist(String playlistId) {
        playlistMapper.deleteByPrimaryKey(playlistId);
    }

    @Override
    public void updatePlaylist(Playlist playlist) {
        Playlist playlistForUpdate = playlistMapper.selectByPrimaryKey(playlist.getId());
        if (playlistForUpdate == null) {
            throw new YunMusicException("歌单不存在");
        }
        PojoUtils.copyProperties(playlist, playlistForUpdate);
        playlistMapper.updateByPrimaryKey(playlistForUpdate);
    }

    @Override
    public void deleteSongForPlaylist(String userId, String playlistId, String songId) {
        Playlist playlist = playlistMapper.selectByPrimaryKey(playlistId);
        if (playlist == null) {
            throw new YunMusicException("歌单不存在");
        }
        if (songMapper.selectByPrimaryKey(songId) == null) {
            throw new YunMusicException("歌曲不存在");
        }
        // 删除关系表，歌单删除歌曲
        Example example = new Example(PlaylistHasSong.class);
        example.createCriteria().andEqualTo("playlistId", playlistId).andEqualTo("songId", songId);
        playlistHasSongMapper.deleteByExample(example);

        // 更新歌单 更新时间
        updatePlaylistTime(playlist);
    }

    @Override
    public void addSongForPlaylist(String userId, String playlistId, String songId) {
        Playlist playlist = playlistMapper.selectByPrimaryKey(playlistId);
        if (playlist == null) {
            throw new YunMusicException("歌单不存在");
        }
        if (songMapper.selectByPrimaryKey(songId) == null) {
            throw new YunMusicException("歌曲不存在");
        }
        // 插入关系表：歌单拥有歌曲
        PlaylistHasSong phs = new PlaylistHasSong();
        phs.setId(Id.nextString());
        phs.setPlaylistId(playlistId);
        phs.setSongId(songId);
        playlistHasSongMapper.insert(phs);

        // 更新歌单 更新时间
        updatePlaylistTime(playlist);
    }

    private void updatePlaylistTime(Playlist playlist) {
        playlist.setUpdateTime(new Date());
        playlistMapper.updateByPrimaryKey(playlist);
    }

    @Override
    public PagedResult selectPlaylist(int pageNum, int pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<Playlist> playlists = playlistMapper.selectAll();

        return PagedResult.pagedResult(playlists);
    }

    @Override
    public PlaylistVO getPlaylistVO(String playlistId) {
        Playlist playlist = playlistMapper.selectByPrimaryKey(playlistId);
        String userId = playlist.getUserId();
        User user = userMapper.selectByPrimaryKey(userId);

        PlaylistVO playlistVO = new PlaylistVO();
        BeanUtils.copyProperties(playlist, playlistVO);

        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(user, userVO);
        userVO.setAvatarImgUrl(FileServerUtils.getUserAvatarUrl(resourceConfig.getFileServer(), user.getAvatarImgId()));
        playlistVO.setUserVO(userVO);

        String coverImgUrl = FileServerUtils.getPlaylistCoverUrl(resourceConfig.getFileServer(), playlist.getCoverImgId());
        playlistVO.setCoverImgUrl(coverImgUrl);

        return playlistVO;
    }

    @Override
    public PlaylistDetailVO getPlaylistDetailVO(String playlistId) {
        Playlist playlist = playlistMapper.selectByPrimaryKey(playlistId);

        List<SongVO> songs = songService.selectSongByPlaylistId(playlistId);

        PlaylistDetailVO playlistDetailVO = new PlaylistDetailVO();
        BeanUtils.copyProperties(playlist, playlistDetailVO);
        playlistDetailVO.setSongs(songs);

        String coverImgUrl = FileServerUtils.getPlaylistCoverUrl(resourceConfig.getFileServer(), playlist.getCoverImgId());
        playlistDetailVO.setCoverImgUrl(coverImgUrl);

        User user = userMapper.selectByPrimaryKey(playlist.getUserId());
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(user, userVO);
        userVO.setAvatarImgUrl(FileServerUtils.getUserAvatarUrl(resourceConfig.getFileServer(), user.getAvatarImgId()));

        playlistDetailVO.setUserVO(userVO);

        return playlistDetailVO;
    }


    @Override
    public PagedResult selectPlaylistVO(Integer pageNum, Integer pageSize, String order) {

        // 设置排序方式
        Example example = new Example(Playlist.class);
        if (order != null && order.equals(OrderConstant.HOT)) {
            example.orderBy("playCount").desc();
        } else if (order != null && order.equals(OrderConstant.TIME)) {
            example.orderBy("updateTime").desc();
        }

        // 分页查询playlist
        PageHelper.startPage(pageNum, pageSize);
        List<Playlist> playlists = playlistMapper.selectByExample(example);  // 这个List其实是Page类型
        // 分页查询结束
        // 通过PageInfo与Page得到总页数，总条数
        PageInfo<?> pageInfo = new PageInfo<>(playlists);
        int pages = pageInfo.getPages();
        long total = pageInfo.getTotal();

        // 得到playlists中每个playlist对应的用户ID
        List<String> userIds = getUserIdsFromPlaylists(playlists);

        // 根据用户id列表查询用户信息
        List<User> users = userMapper.selectUserByIds(userIds);
        // 转换为Map
        Map<String, User> userMap = convertToUserMap(users);
        // 转换成playlistsVO
        List<PlaylistVO> playlistVOs = convertToPlaylistVO(playlists, userMap);

        return new PagedResult(playlistVOs, pageNum, pages, total);
    }


    @Override
    public PagedResult selectSubscribedPlayListByUserId(String userId, Integer pageNum, Integer pageSize) {

        Example example = new Example(Subscribe.class);
        example.createCriteria().andEqualTo("userId", userId)
                .andEqualTo("type", CollectionTypeConstant.SONGLIST);

        // 分页查询
        PageHelper.startPage(pageNum, pageSize);
        List<Subscribe> subscribes = subscribeMapper.selectByExample(example);
        // 分页查询结束
        // 通过PageInfo与Page得到总页数，总条数
        PageInfo<?> pageInfo = new PageInfo<>(subscribes);
        int pages = pageInfo.getPages();
        long total = pageInfo.getTotal();

        // 得到playlist
        List<String> playlistIds = subscribes.stream().map(Subscribe::getTargetId).collect(toList());
        if (playlistIds.size() == 0) {
            return new PagedResult(new ArrayList<>(), pageNum, pages, total);
        }
        List<Playlist> playlists = playlistMapper.selectPlaylistByIds(playlistIds);

        User user = userMapper.selectByPrimaryKey(userId);
        List<PlaylistVO> playlistVOs = convertToPlaylistVO(playlists, user);

        return new PagedResult(playlistVOs, pageNum, pages, total);
    }

    @Override
    public PagedResult selectCreatedPlayListByUserId(String userId, Integer pageNum, Integer pageSize) {
        Example example = new Example(Playlist.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("userId", userId);
        // 分页查询
        PageHelper.startPage(pageNum, pageSize);
        List<Playlist> playlists = playlistMapper.selectByExample(example);
        // 分页查询结束
        // 通过PageInfo与Page得到总页数，总条数
        PageInfo<?> pageInfo = new PageInfo<>(playlists);
        int pages = pageInfo.getPages();
        long total = pageInfo.getTotal();

        User user = userMapper.selectByPrimaryKey(userId);
        List<PlaylistVO> playlistVOs = convertToPlaylistVO(playlists, user);

        return new PagedResult(playlistVOs, pageNum, pages, total);
    }

    @Override
    public Integer getPlaylistCount() {
        return playlistMapper.getPlaylistCount();
    }

    private Map<String, User> convertToUserMap(List<User> users) {
        return users.stream().collect(Collectors.toMap(User::getId, user -> user));
    }

    private List<String> getUserIdsFromPlaylists(List<Playlist> playlists) {
        return playlists.stream().map(Playlist::getUserId).collect(toList());
    }

    private List<PlaylistVO> convertToPlaylistVO(List<Playlist> playlists, User user) {
        return playlists.stream().map(playlist -> {
            PlaylistVO playlistVO = new PlaylistVO();
            BeanUtils.copyProperties(playlist, playlistVO);

            UserVO userVO = new UserVO();
            BeanUtils.copyProperties(user, userVO);
            userVO.setAvatarImgUrl(FileServerUtils.getUserAvatarUrl(resourceConfig.getFileServer(), user.getAvatarImgId()));
            playlistVO.setUserVO(userVO);

            String coverImgUrl = FileServerUtils.getPlaylistCoverUrl(resourceConfig.getFileServer(), playlist.getCoverImgId());
            playlistVO.setCoverImgUrl(coverImgUrl);

            return playlistVO;
        }).collect(toList());
    }

    private List<PlaylistVO> convertToPlaylistVO(List<Playlist> playlists, Map<String, User> userMap) {
        return playlists.stream().map(playlist -> {
            PlaylistVO playlistVO = new PlaylistVO();
            BeanUtils.copyProperties(playlist, playlistVO);

            User user = userMap.get(playlist.getUserId());
            UserVO userVO = new UserVO();
            BeanUtils.copyProperties(user, userVO);
            userVO.setAvatarImgUrl(FileServerUtils.getUserAvatarUrl(resourceConfig.getFileServer(), user.getAvatarImgId()));
            playlistVO.setUserVO(userVO);

            String coverImgUrl = FileServerUtils.getPlaylistCoverUrl(resourceConfig.getFileServer(), playlist.getCoverImgId());
            playlistVO.setCoverImgUrl(coverImgUrl);

            return playlistVO;
        }).collect(toList());
    }


}